﻿using SignalBox.Core;
using SignalBox.Core.Model;
using SignalBox.Core.Model.Graph;
using SignalBox.Core.Model.Timetable;
using SignalBoxEditor.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;

namespace SignalBoxEditor.Controllers
{
    public class EditorController
    {
        private frmSignalboxEditor _parent;
        private TrackLayout _trackLayout;
        private TRACKCELLTYPE _currentTrackTool;

        public delegate void UpdateStatusHandler(string message);
        public event UpdateStatusHandler UpdateStatus;
        public event UpdateStatusHandler UpdateCoords;

        private Point firstSelection;
        private Point popupCoords;
        private Point startSelection;
        private Point endSelection;
        private Rectangle selRect;
        private bool startCopy = false;
        private bool startCut = false;
        private bool hasSelection = false;
        private bool isSelecting = false;
        private bool isMoving = false;

        private bool isMovingLabel = false;
        private TrackText movingText = null;
        private TrackText contextMenuText = null;

        private Dictionary<Point, TrackCell> cells;


        public TRACKCELLTYPE CurrentTrackTool
        {
            get { return _currentTrackTool; }
            set { SetTrackTool(value); }
        }

        private void SetTrackTool(TRACKCELLTYPE tt)
        {
            if (_currentTrackTool != tt)
            {
                popupCoords = Point.Empty;
                startSelection = Point.Empty;
                endSelection = Point.Empty;
                hasSelection = false;
                isMoving = false;
                isSelecting = false;
                _parent.LayoutPanel.SetSelection(startSelection, endSelection);
            }

            _currentTrackTool = tt;
            SetStatusMessage("");

            if (_currentTrackTool == TRACKCELLTYPE.LINKTRACK)
            {
                firstSelection = Point.Empty;
                SetStatusMessage("Select first track element");
            }
        }

        public EditorController(frmSignalboxEditor parent)
        {
            _parent = parent;
            _trackLayout = new TrackLayout(true);
            _currentTrackTool = TRACKCELLTYPE.SELECT;

            _parent.LayoutPanel.MouseDown += LayoutPanel_MouseDown;
            _parent.LayoutPanel.MouseMove += LayoutPanel_MouseMove;
            _parent.LayoutPanel.MouseUp += LayoutPanel_MouseUp;

        }

        private bool CheckModifications()
        {
            if (_trackLayout.HasModifications)
            {
                DialogResult dr = MessageBox.Show("Do you want to save your changes ?", "Save changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dr == DialogResult.Cancel)
                    return false;
                if (dr == DialogResult.Yes)
                {
                    SaveLayout();
                }
                return true;

            }

            return true;
        }

        void LayoutPanel_MouseDown(object sender, MouseEventArgs e)
        {
            Point p = _parent.LayoutPanel.GetCell(e.Location);
            if (e.Button == MouseButtons.Left)
            {
                if (startCopy || startCut)
                    return;

                if (_currentTrackTool == TRACKCELLTYPE.SELECT)
                {
                    if (hasSelection)
                    {
                        if (selRect.Contains(p))
                        {
                            fetchCellsForCurrentOperation();
                            startSelection = p;
                            isMoving = true;
                            return;
                        }
                    }
                    startSelection = p;
                    isSelecting = true;
                }
                else if (_currentTrackTool == TRACKCELLTYPE.TEXTTOOL)
                {

                    TrackText tt = _parent.LayoutPanel.IsHitLabel(_parent.LayoutPanel.ConvertScreenToClient(e.Location));

                    if (tt != null)
                    {

                        isMovingLabel = true;
                        movingText = tt;
                    }
                }
            }
        }

        void LayoutPanel_MouseMove(object sender, MouseEventArgs e)
        {

            Point p = _parent.LayoutPanel.GetCell(e.Location);


            string extraInfo = "";

            TrackCell tc = _trackLayout.getCell(p.X, p.Y);
            if (tc != null)
            {
                extraInfo += " - Length: " + tc.Length.ToString() + "m";
                if (tc.Name != "")
                    extraInfo += " - " + tc.Name;
                if (tc.Platform != "")
                    extraInfo += "@" + tc.Platform;
            }

            SetCoords(String.Format("X: {0} - Y: {1}", p.X, p.Y) + extraInfo);

            if (isSelecting)
            {
                endSelection = p;
                _parent.LayoutPanel.SetSelection(startSelection, endSelection);
                SetSelRect();
            }
            else if (isMovingLabel)
            {
                Point loc = _parent.LayoutPanel.ConvertScreenToClient(e.Location);
                movingText.Location = loc;
                _parent.LayoutPanel.Invalidate();
            }
            else if (isMoving)
            {
                endSelection = p;
                int offsetX = endSelection.X - startSelection.X;
                int offsetY = endSelection.Y - startSelection.Y;


                removeCellsInCurrentSelection(offsetX, offsetY);

                foreach (Point point in cells.Keys)
                {
                    TrackCell cell = cells[point];


                    cell.X += offsetX;
                    cell.Y += offsetY;
                    _trackLayout.updateCell(cell.X, cell.Y, cells[point]);
                }

                selRect.Offset(offsetX, offsetY);
                _parent.LayoutPanel.SetSelection(selRect);
                _parent.LayoutPanel.Invalidate();

                startSelection = endSelection;


            }

        }

        void LayoutPanel_MouseUp(object sender, MouseEventArgs e)
        {
            Point p = _parent.LayoutPanel.GetCell(e.Location);
            if (e.Button == MouseButtons.Left)
            {
                if (startCut)
                {
                    pasteCellsTo(p);
                    startCut = false;
                    SetStatusMessage("");
                    return;
                }
                else if (startCopy)
                {
                    copyCellsTo(p);
                    startCopy = false;
                    SetStatusMessage("");
                }
                
                if (_currentTrackTool != TRACKCELLTYPE.SELECT && _currentTrackTool != TRACKCELLTYPE.LINKTRACK && _currentTrackTool != TRACKCELLTYPE.TEXTTOOL)
                {
                    TrackCell tc = new TrackCell(true);
                    tc.X = p.X;
                    tc.Y = p.Y;
                    tc.Type = _currentTrackTool;
                    TrackLayout.updateCell(p.X, p.Y, tc);
                }
                else
                {
                    if (_currentTrackTool == TRACKCELLTYPE.SELECT)
                    {
                        if (isMoving)
                        {
                            isMoving = false;
                            cells.Clear();
                        }
                        else
                        {
                            endSelection = p;
                            if (startSelection != endSelection)
                            {
                                _parent.LayoutPanel.SetSelection(startSelection, endSelection);
                            }
                            else
                            {
                                _parent.LayoutPanel.ClearSelection();
                            }
                            SetSelRect();
                            isSelecting = false;
                        }
                    }
                    else if (_currentTrackTool == TRACKCELLTYPE.LINKTRACK)
                    {
                        if (firstSelection == Point.Empty)
                        {
                            if (_trackLayout.getCell(p.X, p.Y) != null)
                            {
                                firstSelection = p;
                                _trackLayout.markCellSelected(p.X, p.Y, true);
                                SetStatusMessage("Select second track element");
                            }
                        }
                        else
                        {
                            if (_trackLayout.getCell(p.X, p.Y) != null)
                            {
                                _trackLayout.markCellSelected(firstSelection.X, firstSelection.Y, false);
                            }

                            if (!_trackLayout.linkCells(firstSelection.X, firstSelection.Y, p.X, p.Y))
                            {
                                MessageBox.Show(_parent, "Cannot link cells", "Link Cells", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }

                            firstSelection = Point.Empty; ;

                            SetStatusMessage("Select first track element");

                        }
                    }
                    else if (_currentTrackTool == TRACKCELLTYPE.TEXTTOOL)
                    {

                        if (isMovingLabel)
                        {
                            movingText = null;
                            isMovingLabel = false;
                            return;
                        }

                        Point lblLoc = _parent.LayoutPanel.ConvertScreenToClient(e.Location);

                        TrackText tt = _parent.LayoutPanel.IsHitLabel(lblLoc);

                        frmTextInput frm = new frmTextInput();
                        if (tt != null)
                        {
                            frm.EnteredText = tt.Text;
                            frm.SelectedFont = tt.Font;
                            frm.SelectedColor = tt.Color;

                        }

                        DialogResult result = frm.ShowDialog();
                        if (result == DialogResult.OK)
                        {

                            if (tt == null)
                            {
                                tt = new TrackText();
                                tt.Location = lblLoc;
                                _trackLayout.Labels.Add(tt);
                                _trackLayout.HasModifications = true;
                            }
                            tt.Color = frm.SelectedColor;
                            tt.Font = frm.SelectedFont;
                            tt.Text = frm.EnteredText;
                        }
                        
                    }
                }
            }
            else if (e.Button == MouseButtons.Middle)
            {
                TrackLayout.removeCell(p.X, p.Y);

            }
            else if (e.Button == MouseButtons.Right)
            {
                TrackCell tc = _trackLayout.getCell(p.X, p.Y);

                ContextMenu ctx = new ContextMenu();

                if (tc != null && tc.TrackCellType.TheTrackCellType != TRACKCELLTYPE.PLATFORM)
                {
                    ctx.MenuItems.Add("Properties", new EventHandler(mnuProperties));

                    if (tc.TrackCellType.Category == "SIGNAL")
                    {
                        MenuItem mnuSA = new MenuItem("Signal Type");
                        ctx.MenuItems.Add(mnuSA);

                        MenuItem mnuTwo = new MenuItem("Two");
                        mnuTwo.RadioCheck = true;
                        if (tc.SignalAspect == SIGNALASPECTTYPE.TWO)
                        {
                            mnuTwo.Checked = true;
                        }
                        mnuTwo.Click += mnuTwo_Click;
                        mnuSA.MenuItems.Add(mnuTwo);

                        MenuItem mnuThree = new MenuItem("Three");
                        mnuThree.RadioCheck = true;
                        if (tc.SignalAspect == SIGNALASPECTTYPE.THREE)
                        {
                            mnuThree.Checked = true;
                        }
                        mnuThree.Click += mnuThree_Click;
                        mnuSA.MenuItems.Add(mnuThree);


                        MenuItem mnuFour = new MenuItem("Four");
                        mnuFour.RadioCheck = true;
                        if (tc.SignalAspect == SIGNALASPECTTYPE.FOUR)
                        {
                            mnuFour.Checked = true;
                        }
                        mnuFour.Click += mnuFour_Click;
                        mnuSA.MenuItems.Add(mnuFour);

                    }

                    ctx.MenuItems.Add("-");
                }

                ctx.MenuItems.Add("Cut", new EventHandler(Cut));
                ctx.MenuItems.Add("Copy", new EventHandler(Copy));
                ctx.MenuItems.Add("Delete", new EventHandler(Delete));


                TrackText text = _parent.LayoutPanel.IsHitLabel(e.Location);

                if (text != null)
                {
                    ctx.MenuItems.Add(new MenuItem("-"));
                    ctx.MenuItems.Add(new MenuItem("Text properties", new EventHandler(TextProperties)));
                    ctx.MenuItems.Add(new MenuItem("Text delete", new EventHandler(TextDelete)));
                    contextMenuText = text;
                }

                ctx.Show(_parent.LayoutPanel, new Point(e.X, e.Y));
                popupCoords = p;


            }
            _parent.LayoutPanel.Invalidate();
        }

        private void copyCellsTo(Point coords)
        {
            fetchCellsForCurrentOperation();
            int offsetX = coords.X - startSelection.X;
            int offsetY = coords.Y - startSelection.Y;

            foreach (Point p in cells.Keys)
            {
                TrackCell tc = (TrackCell)cells[p].Clone();

                tc.X += offsetX;
                tc.Y += offsetY;

                _trackLayout.updateCell(tc.X, tc.Y, tc);
            }

            _parent.LayoutPanel.Invalidate();
            ClearSelection();
        }

        private void pasteCellsTo(Point coords)
        {
            fetchCellsForCurrentOperation();
            removeCellsInCurrentSelection(0, 0);
            int offsetX = coords.X - startSelection.X;
            int offsetY = coords.Y - startSelection.Y;

            foreach (Point p in cells.Keys)
            {
                TrackCell tc = cells[p];

                tc.X += offsetX;
                tc.Y += offsetY;

                _trackLayout.updateCell(tc.X, tc.Y, tc);

            }

            _parent.LayoutPanel.Invalidate();
            ClearSelection();
        }

        void mnuFour_Click(object sender, EventArgs e)
        {
            TrackCell tc = _trackLayout.getCell(popupCoords.X, popupCoords.Y);
            tc.SignalAspect = SIGNALASPECTTYPE.FOUR;
        }

        void mnuThree_Click(object sender, EventArgs e)
        {
            TrackCell tc = _trackLayout.getCell(popupCoords.X, popupCoords.Y);
            tc.SignalAspect = SIGNALASPECTTYPE.THREE;
        }

        void mnuTwo_Click(object sender, EventArgs e)
        {
            TrackCell tc = _trackLayout.getCell(popupCoords.X, popupCoords.Y);
            tc.SignalAspect = SIGNALASPECTTYPE.TWO;
        }

        private void mnuProperties(object sender, EventArgs args)
        {
            TrackCell tc = _trackLayout.getCell(popupCoords.X, popupCoords.Y);

            frmTrackProperties frm = new frmTrackProperties(tc);
            frm.StartPosition = FormStartPosition.CenterParent;
            frm.ShowDialog(_parent);
        }

        public void TextProperties(object sender, EventArgs args)
        {
            if (contextMenuText == null) return;

            frmTextInput frm = new frmTextInput();
            if (contextMenuText != null)
            {
                frm.EnteredText = contextMenuText.Text;
                frm.SelectedFont = contextMenuText.Font;
                frm.SelectedColor = contextMenuText.Color;

            }

            DialogResult result = frm.ShowDialog();
            if (result == DialogResult.OK)
            {


                contextMenuText.Color = frm.SelectedColor;
                contextMenuText.Font = frm.SelectedFont;
                contextMenuText.Text = frm.EnteredText;
            }
        }
        public void TextDelete(object sender, EventArgs args)
        {
            if (contextMenuText == null) return;
            _trackLayout.Labels.Remove(contextMenuText);
            _trackLayout.HasModifications = true;
        }

        public void Cut(object sender, EventArgs args)
        {
            startCut = true;
            SetStatusMessage("Select location to paste cells");
        }

        public void Copy(object sender, EventArgs args)
        {
            startCopy = true;
            SetStatusMessage("Select location to copy cells to");

        }

        private void Delete(object sender, EventArgs args)
        {
            if (hasSelection && selRect.Contains(popupCoords))
            {
                fetchCellsForCurrentOperation();
                removeCellsInCurrentSelection(0, 0);
                cells.Clear();

            }
            else
            {
                _trackLayout.removeCell(popupCoords.X, popupCoords.Y);
            }
            _parent.LayoutPanel.Invalidate();
        }



        private void fetchCellsForCurrentOperation()
        {

            cells = new Dictionary<Point, TrackCell>();

            for (int x = selRect.X; x < selRect.X + selRect.Width; x++)
            {
                for (int y = selRect.Y; y < selRect.Y + selRect.Height; y++)
                {
                    TrackCell tc = _trackLayout.getCell(x, y);
                    if (tc != null)
                    {

                        cells[new Point(x, y)] = tc;

                    }
                }
            }
        }

        private void removeCellsInCurrentSelection(int offsetX, int offsetY)
        {
            for (int x = selRect.X + offsetX; x <= selRect.X + selRect.Width + offsetX; x++)
            {
                for (int y = selRect.Y + offsetY; y <= selRect.Y + selRect.Height + offsetY; y++)
                {
                    _trackLayout.removeCell(x, y);
                }
            }
        }

        private void ClearSelection()
        {
            startSelection = Point.Empty;
            endSelection = Point.Empty;
            SetSelRect();
        }


        private void SetSelRect()
        {
            if (startSelection == Point.Empty || endSelection == Point.Empty)
            {
                selRect = Rectangle.Empty;
                hasSelection = false;
                _parent.LayoutPanel.SetSelection(selRect);
                return;
            }
            if (startSelection == endSelection)
            {
                selRect = Rectangle.Empty;
                hasSelection = false;
                _parent.LayoutPanel.SetSelection(selRect);
                return;
            }
            Rectangle rect = new Rectangle();
            rect.Location = new Point(Math.Min(startSelection.X, endSelection.X), Math.Min(startSelection.Y, endSelection.Y));
            rect.Size = new Size(Math.Abs(endSelection.X - startSelection.X), Math.Abs(endSelection.Y - startSelection.Y));
            selRect = rect;
            hasSelection = true;
            _parent.LayoutPanel.SetSelection(selRect);
        }




        private void SetCoords(string message)
        {
            if (UpdateCoords != null)
                UpdateCoords(message);
        }

        private void SetStatusMessage(string message)
        {
            if (UpdateStatus != null)
                UpdateStatus(message);
        }




        public TrackLayout TrackLayout
        {
            get { return _trackLayout; }
        }

        public void NewLayout()
        {
            if (!CheckModifications())
                return;

            _trackLayout = new TrackLayout(true);
            _parent.LayoutPanel.TrackLayout = _trackLayout;
        }
        public void LoadLayout(FileInfo fi)
        {
            if (!CheckModifications())
                return;

            _trackLayout.loadLayout(fi);

        }

        public void SaveLayout(FileInfo fi)
        {
            _trackLayout.Save(fi);
        }
        public void SaveLayout()
        {
            _trackLayout.save();
        }
        public void ValidateLayout()
        {
            LayoutGraph lg = new LayoutGraph(TrackLayout, true);
            if (!lg.isValid())
            {
                MessageBox.Show("Invalid layout", "Layout", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                MessageBox.Show("Layout valid", "Layout", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        public void LayoutSettings()
        {
            frmLayoutSettings frm;

            frm = new frmLayoutSettings(_trackLayout);
            frm.StartPosition = FormStartPosition.CenterParent;
            frm.ShowDialog(_parent);

            _parent.LayoutPanel.Invalidate();
        }

        public void NewTimetable()
        {
            Timetable tt = new Timetable(_trackLayout);
            frmTimetable frm = new frmTimetable(tt);
            frm.StartPosition = FormStartPosition.CenterParent;
            frm.ShowDialog(_parent);
        }
        public void OpenTimetable()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Signalbox Timetable (*.stt)|*.stt|All Files (*.*)|*.*";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Timetable tt = new Timetable(_trackLayout);
                tt.FileName = dlg.FileName;
                tt.Load();
                frmTimetable frm = new frmTimetable(tt);
                frm.StartPosition = FormStartPosition.CenterParent;
                frm.ShowDialog(_parent);
            }
        }
    }
}
